﻿#region License
/*
Copyright (c) 2009, Open University of the Netherlands
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided 
that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the 
  following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
  the following disclaimer in the documentation and/or other materials provided with the distribution.

* Neither the name of Open University of the Netherlands nor the names of its contributors may be 
  used to endorse or promote products derived from this software without specific prior written 
  permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
*/
#endregion

namespace EwbDesigner.NET
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Drawing;
    using System.IO;

    using BrightIdeasSoftware;
    using Ionic.Zip;

    using EwbDesigner.NET.Classes;

    using WeifenLuo.WinFormsUI.Docking;

    using ZipNode = System.Collections.Generic.SimpleTreeNode<EwbDesigner.NET.Classes.DirEntry>;

    public partial class ArchiveForm : DockContent
    {
        #region Fields

        static readonly ArchiveForm _instance = new ArchiveForm();

        private ZipTree Collection = new ZipTree();
        private Hashtable fIcons = new Hashtable();

        #endregion Fields

        #region Constructors

        private ArchiveForm()
        {
            InitializeComponent();

            HideOnClose = true;

            Collection.PathProperty = "Directory";
            Collection.PathSeparator = @"\";
        }

        #endregion Constructors

        #region Properties

        //Visible when reflecting
        public static ArchiveForm Instance
        {
            get { return _instance; }
        }

        public String Content
        {
            get
            {
                return fContent;
            }

            set
            {
                ProcessZipFile(value);
            }
        }
        private string fContent = "";

        private TreeListView Structure
        {
            get { return Instance.treeListView1; }
        }

        #endregion Properties

        #region Event Handlers

        private void ArchiveForm_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e)
        {
            //if (!Structure.IsDisposed)
            //{
            //    Structure.SetObjects(null);
            //}

            //if (!Collection.IsDisposed)
            //{
            //    Collection.Clear();
            //}
        }

        private void TreeForm_Load(object sender, EventArgs e)
        {
            /////////////////////////
#if DEBUG
            if (this.MdiParent != null)
            {
                Debug.WriteLine("MdiParent Present");
            }
            else
            {
                Debug.WriteLine("MdiParent Missing");
            }
#endif
            ModelDelegates();
        }

        #endregion Event Handlers

        #region Methods

        private void ModelDelegates()
        {
            /////////////////////////

            // Can the given object be expanded?
            Structure.CanExpandGetter = delegate(Object x)
            {
                if (x is ZipNode)
                {
                    return ((ZipNode)x).Value.IsDirectory;
                }
                return false;
            };

            // What objects should belong underneath the given model object?
            Structure.ChildrenGetter = delegate(Object x)
            {
                return ((ZipNode)x).Children;

                throw new ArgumentException("Should be ZipNode");
            };

            /////////////////////////
            //0: FileName
            /////////////////////////

            olvColumn1.AspectGetter = delegate(object x)
            {
                return x;
            };

            olvColumn1.AspectToStringConverter = delegate(object x)
            {
                if (x is ZipNode)
                {
                    if ((((ZipNode)x).Value).IsDirectory)
                    {
                        return String.Format("[{0}]", ((ZipNode)x).Value.Directory);
                    }
                    else
                    {
                        return ((ZipNode)x).Value.FileName;
                    }
                }

                throw new ArgumentException("Should be ZipNode");
            };

            olvColumn1.ImageGetter = delegate(object x)
            {
                String path = ((ZipNode)x).Value.FileName;
                String extension = Path.GetExtension(path);
                if (extension != null)
                {
                    if (fIcons.ContainsKey(extension) == false)
                    {
                        Icon icon = ShellIcon.GetSmallIcon(path);
                        imageList1.Images.Add(icon);
                        fIcons.Add(extension, imageList1.Images.Count - 1);
                    }

                    return (int)fIcons[extension];
                }
                else
                {
                    //0 = FolderOpen (WinXP)
                    return 0;
                }
            };

            /////////////////////////
            //1: Compression Method
            /////////////////////////

            olvColumn2.AspectGetter = delegate(object x)
            {
                return x;
            };

            olvColumn2.AspectToStringConverter = delegate(object x)
            {
                if (x is ZipNode)
                {
                    return ((ZipNode)x).Value.CompressionMethod;
                }
                else
                {
                    return "error";
                }
            };

            /////////////////////////
            //2: Uncompressed Size
            /////////////////////////

            olvColumn3.AspectGetter = delegate(object x)
            {
                return x;
            };

            olvColumn3.AspectToStringConverter = delegate(object x)
            {
                if (x is ZipNode)
                {
                    if (((ZipNode)x).Value.IsDirectory)
                    {
                        return "";
                    }
                    else
                    {
                        return ((ZipNode)x).Value.UncompressedSize.ToString();
                    }
                }
                else
                {
                    return "error";
                }
            };

            /////////////////////////
        }

        private void ProcessZipFile(String value)
        {
            //TODO: All these can give a ArgumentOutOfRange Exception in TreeListView.RemoveObjects()
            //Structure.ClearObjects();
            //Structure.RemoveObjects(Collection.Children);

            Collection.Root.Clear();
            Collection.Clear();

            Structure.BeginUpdate();

            fContent = value;
            if (!String.IsNullOrEmpty(value))
            {

                ZipFile Zip = new ZipFile(Content);

                Collection.Root.Value = new DirEntry("", true);

                //Add any node directories...
                //Try to find the longest path and add the remainder.
                foreach (ZipEntry zipentry in Zip)
                {
                    String path = "";
                    String file = "";

                    Boolean isDirectory = zipentry.IsDirectory;
                    if (isDirectory)
                    {
                        file = "";
                        path = Path.GetDirectoryName(zipentry.FileName.Replace('/', Path.DirectorySeparatorChar));
                    }
                    else
                    {
                        file = Path.GetFileName(zipentry.FileName.Replace('/', Path.DirectorySeparatorChar));
                        path = Path.GetDirectoryName(zipentry.FileName.Replace('/', Path.DirectorySeparatorChar));
                    }

                    String[] patharr = path.Split(new Char[] { '\\' });
                    String tried = path;

                    ZipNode last = Collection.NodeByPath(path);
                    if (last == null)
                    {
                        last = Collection.Root;
                        do
                        {
                            if (Collection.NodeByPath(tried) == null && patharr.Length > 0)
                            {
                                Array.Resize(ref patharr, patharr.Length - 1);
                                tried = String.Join(@"\", patharr);
                            }
                            else
                            {
                                last = Collection.NodeByPath(tried);
                                String[] tmp = path.Split(new Char[] { '\\' });
                                for (Int32 i = patharr.Length; i < tmp.Length; i++)
                                {
                                    last = last.Children.Add(new DirEntry(tmp[i], true));
                                }
                                break;
                            }
                        } while (true);
                    }

                    //Add any leaf files...
                    if (!isDirectory)
                    {
                        last = last.Children.Add(new DirEntry(zipentry));
                    }
                }

                Debug.WriteLine("Updating Archive");

                Zip.Dispose();
                Zip = null;
            }

            Structure.EndUpdate();
            
            Structure.ClearObjects();
            Structure.SetObjects(Collection.Children);
            Structure.Update();
            Update();
        }

        #endregion Methods

        private void openArchiveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ProcessStartInfo psi = new ProcessStartInfo();

            psi.UseShellExecute = true;
            psi.FileName = fContent;
            psi.Verb = "open";
            System.Diagnostics.Process.Start(psi);
        }
    }
}